<?php


namespace app\middleware;


use app\cms\model\adminUserModel;
use iflow\App;
use iflow\exception\Adapter\HttpException;
use iflow\Helper\Tools\System;
use iflow\middleware\Middleware;
use iflow\Response;
use think\db\ConnectionInterface;
use think\facade\Db;

/**
 * 系统安装中间件
 * Class Install
 * @package app\middleware
 */
#[Middleware]
class Install {

    protected array $config = [];

    protected array $connection = [];

    protected ConnectionInterface $db;

    protected string $database = '';

    protected array $ext = [
        'zlib',
        'bcmath',
        'mbstring',
        'json',
        'fileinfo',
        'curl',
        'sockets'
    ];

    protected array $dir = [
        'runtime'
    ];

    protected array $saveDbConfig = [
        '${__INSTALL__HOST__}' => 'hostname',
        '${__INSTALL__DATABASE__}' => 'database',
        '${__INSTALL__USERNAME__}' => 'username',
        '${__INSTALL__PASSWORD__}' => 'password',
        '${__INSTALL__PORT__}' => 'hostport'
    ];

    /**
     * @param App $app
     * @param $next
     * @return Response|bool
     * @throws \Exception
     */
    public function handle(App $app, $next): Response|bool {
        $this->initDbConnection();
        if (session('install_step') === 'set_admin_user') {
            session('install_step', null);
        }

        // 验证系统是否已安装
        if ($this->validInstalled() && !session('install_step')) {
            $this->config['connections'][$this->config['default'] ?? ''] = $this->connection;
            Db::setConfig($this->config);
            Db::execute("use `{$this->connection['database']}`");
            return $next($app) ?: true;
        }

        // 跳转至安装页面
        return $this->install();
    }

    /**
     * 验证系统是否已经安装完毕
     * @return bool
     */
    public function validInstalled(): bool {
        $installLock = app() -> getRootPath() . 'install.lock';
        // 安装成功
        if (file_exists($installLock) || $this->systemDataInstalled()) {
            return true;
        }
        return false;
    }

    /**
     * 当未安装时 进入安装引导页面
     * @return Response
     * @throws \Exception
     */
    public function install(): Response {
        // 当无SESSION
        if (!session('install_step')) {
            session('install_step', 'init');
        }

        return match (request() -> params('install')) {
            'db' => $this->buildDbDatabase(),
            'config' => $this->setConfig(request() -> postParams('config') ?: []),
            'user' => $this->setSystemAdminAccount(request() -> postParams('user') ?: []),
            default => view('install/index', [
                'install_dose_not_ext' => $this->getProjectNecessaryExtension(),
                'install_ext' => $this->ext,
                'sessionToken' => \iflow\facade\Session::getSessionId(),
                'dir_write' => array_map(function ($value) {
                    $path = app() -> getRootPath() . $value . "/readme";
                    return [$value, is_writable($path)];
                }, $this->dir)
            ])
        };
    }

    /**
     * 验证系统数据库是否安装完毕
     * @return bool
     */
    public function systemDataInstalled(): bool {
        $database = $this->db -> query("show databases like '{$this -> database}'");
        if (!$database) return false;

        $this->installLock();
        return true;
    }

    // 写入安装锁
    protected function installLock() {
        $installLock = app() -> getRootPath() . 'install.lock';
        file_put_contents($installLock, $this->database);
    }

    /**
     * 初始化 数据库链接
     * @return $this
     */
    protected function initDbConnection(): static
    {
        $this->config = config('database');
        $this->connection = $this->config['connections'][$this->config['default'] ?? ''] ?? [];
        $this->database = $this->connection['database'];

        if (empty($this->connection)) {
            throw new HttpException(502, '数据库配置不存在');
        }

        $this->config['connections'][$this->config['default'] ?? '']['database'] = '';

        Db::setConfig($this->config);
        $this->db = Db::connect($this->config['default']);

        return $this;
    }

    /**
     * 保存配置文件
     * @param string $content
     * @return bool
     */
    protected function arrayToFile(string $content): bool {
        return file_put_contents(
            app() -> getConfigPath() . 'database.php', $content
        );
    }

    /**
     * 获取当前项目安装必要扩展
     * @return array
     */
    protected function getProjectNecessaryExtension(): array {
        return System::extensionLoaded($this->ext);
    }

    /**
     * 初始化当前系统数据
     * @return Response
     * @throws \Exception
     */
    protected function buildDbDatabase(): Response {

        if (session('install_step') === 'set_config') {
            ini_set('max_execution_time', 6000);

            $this -> db -> execute("create database `{$this->connection['database']}`");
            $this -> db -> execute("use `{$this->connection['database']}`");

            load_sql_save_to_server($this->db, app()->getRootPath() . 'install/database/tables');
            $this->installLock();
            session('install_step', 'install_db');
            return message() -> success('数据库安装成功');
        }
        return message() -> parameter_error('请先设置数据库配置');
    }

    /**
     * 设置当前服务数据库配置
     * @param array $config
     * @return Response
     * @throws \Exception
     */
    protected function setConfig(array $config = []): Response {
        $configContent = file_get_contents(
            \app() -> getRootPath() . '/resource/default/database_default.php'
        );

        foreach ($this->saveDbConfig as $replaceKey => $arrayKey) {
            $configContent = str_replace($replaceKey, $config[$arrayKey], $configContent);
        }

        $this->arrayToFile($configContent);
        session('install_step', 'set_config');
        return message() -> success('配置文件设置成功', [
            'install_step' => session('install_step')
        ]);
    }

    /**
     * 更新当前系统管理员账户
     * @param array $user
     * @return Response
     * @throws \Exception
     */
    protected function setSystemAdminAccount(array $user): Response {
        if (session('install_step') === 'install_db') {
            // TODO: 设置当前系统管理员账户
            Db::execute("use `{$this->connection['database']}`");
            session('install_step', 'set_admin_user');
            return (new adminUserModel()) -> updateInfo([
                'user_name' => $user['user_name'],
                'password' => password_hash($user['password'], PASSWORD_DEFAULT),
                'avatar' => $user['avatar']
            ], [
                'uid' => 1
            ]);
        }
        return message() -> parameter_error('当前系统数据库尚未安装');
    }
}